
下一组特征测试单类型的其他属性，以及可能适用于某些操作(例如值交换)。

\subsubsubsection{D.3.1\hspace{0.2cm}其他类型的属性}

\textbf{std::is\_signed<T>::value}

\begin{itemize}
\item 
若T是有符号算术类型(即包含负数表示的算术类型;这包括(signed) int, float)等类型。

\item 
对于bool类型，会产生false。

\item 
对于char类型，其结果由具体实现定义。

\item 
对于所有非算术类型(包括枚举类型)，is\_signed会生成false。
\end{itemize}

\textbf{std::is\_unsigned<T>::value}

\begin{itemize}
\item 
若T是无符号算术类型(即不包含负数表示的算术类型;这包括unsigned int和bool类型)。

\item 
对于char类型，其结果由具体实现定义。

\item 
对于所有非算术类型(包括枚举类型)，is\_unsigned会生成false。
\end{itemize}

\textbf{std::is\_const<T>::value}

\begin{itemize}
\item 
若类型是const的，则生成true。

\item 
const指针具有const类型，而非const指针或指向const类型的引用则不具有const类型。例如:
\begin{lstlisting}[style=styleCXX]
is_const<int* const>::value // true
is_const<int const*>::value // false
is_const<int const&>::value // false
\end{lstlisting}

\item 
若元素类型是const的，则该将数组定义为const的。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}C++11发布后，核心问题1059的解决方案澄清了这一点。
\end{tcolorbox}

例如:
\begin{lstlisting}[style=styleCXX]
is_const<int[3]>::value // false
is_const<int const[3]>::value // true
is_const<int[]>::value // false
is_const<int const[]>::value // true
\end{lstlisting}

\end{itemize}

\begin{table}[H]
	\centering
	\begin{tabular}{|l|l|}
		\hline
		\textbf{特征}                                                  & \textbf{作用}                                                                                                            \\ \hline
		is\_signed\textless{}T \textgreater{}                           & 有符号算术类型                                                                                                     \\ \hline
		is\_unsigned\textless{}T \textgreater{}                         & 无符号算术类型                                                                                                   \\ \hline
		is\_const\textless{}T \textgreater{}                            & 常量(不可变)限定                                                                                                            \\ \hline
		is\_volatile\textless{}T \textgreater{}                         & 可变限定                                                                                                         \\ \hline
		is\_aggregate\textless{}T \textgreater{}                        & 聚合类型(C++17)                                                                                               \\ \hline
		is\_trivial\textless{}T \textgreater{}                          & 标量、普通类或这些类型的数组                                                                            \\ \hline
		is\_trivially\_copyable\textless{}T \textgreater{}              & 标量、可简单复制的类或这些类型的数组                                                                 \\ \hline
		is\_standard\_layout\textless{}T \textgreater{}                 & 标量、标准布局类或这些类型的数组                                                                    \\ \hline
		is\_pod\textless{}T \textgreater{}                              & \begin{tabular}[c]{@{}l@{}}普通旧数据类型(memcpy()用于复制对象的类型)\end{tabular}                  \\ \hline
		is\_literal\_type\textless{}T \textgreater{}                    & \begin{tabular}[c]{@{}l@{}}类型的标量、引用、类或数组(C++17起弃用)\end{tabular}      \\ \hline
		is\_empty\textless{}T \textgreater{}                            & \begin{tabular}[c]{@{}l@{}}没有成员、虚成员函数或虚基类的类\end{tabular}         \\ \hline
		is\_polymorphic\textless{}T \textgreater{}                      & 具有(派生)虚成员函数的类                                                                             \\ \hline
		is\_abstract\textless{}T \textgreater{}                         & 抽象类(至少一个纯虚函数)                                                                        \\ \hline
		is\_final\textless{}T \textgreater{}                            & \begin{tabular}[c]{@{}l@{}}最后一个类(不允许派生的类，C++14)\end{tabular}                    \\ \hline
		has\_virtual\_destructor\textless{}T \textgreater{}             & 具有虚析构函数的类                                                                                              \\ \hline
		has\_unique\_object\_representations\textless{}T \textgreater{} & \begin{tabular}[c]{@{}l@{}}两个具有相同值的对象，在内存中具有相同的表示形式(C++17)\end{tabular} \\ \hline
		alignment\_of\textless{}T \textgreater{}                        & 相当于alignof (T)                                                                                                   \\ \hline
		rank\textless{}T \textgreater{}                                 & 数组类型的维数(或0)                                                                               \\ \hline

		extent\textless{}T,I=0 \textgreater{}                           & 维度I(或0)的范围                                                                                               \\ \hline
		underlying\_type\textless{}T \textgreater{}                     & 枚举类型的基础类型                                                                                     \\ \hline
	\end{tabular}
\end{table}

\begin{center}
表D.3. 特征测试简单类型的属性
\end{center}

\begin{table}[H]
	\centering
	\begin{tabular}{|l|l|}
		\hline
		is\_invocable\textless{}T,Args... \textgreater{}                & 用作可调用的Args…(C++ 17)                                                                          \\ \hline
		is\_nothrow\_invocable\textless{}T,Args... \textgreater{}       & \begin{tabular}[c]{@{}l@{}}用作可调用的Args…不抛出异常(C++17)\end{tabular}               \\ \hline
		is\_invocable\_r\textless{}RT,T,Args... \textgreater{}          & \begin{tabular}[c]{@{}l@{}}用作可调用的Args…返回RT(C++17)\end{tabular}                   \\ \hline
		is\_nothrow\_invocable\_r\textless{}RT,T,Args... \textgreater{} & \begin{tabular}[c]{@{}l@{}}用作可调用的Args…返回RT而不抛出异常(C++17)\end{tabular} \\ \hline
		invoke\_result\textless{}T,Args... \textgreater{}               & \begin{tabular}[c]{@{}l@{}}结果类型用作可调用的Args…(C++17)\end{tabular}                        \\ \hline
		result\_of\textless{}F,ArgTypes \textgreater{}                  & \begin{tabular}[c]{@{}l@{}}使用参数类型ArgTypes调用F的结果类型(C++17起弃用)\end{tabular}   \\ \hline
	\end{tabular}
\end{table}

\begin{center}
表D.3. 特征测试简单类型的属性(续)
\end{center}

\textbf{std::is\_volatile<T>::value}

\begin{itemize}
\item 
若类型是变量限定，则生成true。

\item 
可变指针具有可变限定的类型，而非可变指针或对可变类型的引用不是可变限定的。例如:

\begin{lstlisting}[style=styleCXX]
is_volatile<int* volatile>::value // true
is_volatile<int volatile*>::value // false
is_volatile<int volatile&>::value // false
\end{lstlisting}

\item 
若元素类型是可变限定的，语言将数组定义为可变限定的。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}C++11发布后，核心问题1059的解决方案澄清了这一点。
\end{tcolorbox}

例如:
\begin{lstlisting}[style=styleCXX]
is_volatile<int[3]>::value // false
is_volatile<int volatile[3]>::value // true
is_volatile<int[]>::value // false
is_volatile<int volatile[]>::value // true
\end{lstlisting}
\end{itemize}

\textbf{std::is\_aggregate<T>::value}

\begin{itemize}
\item 
若T是聚合类型(数组或没有用户定义的类型、显式的或继承的构造函数的class/struct/union，没有私有的或受保护的非静态数据成员，没有虚函数，没有虚拟的、私有的或受保护的基类)，则生成true。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}聚合的基类和/或数据成员不必聚合。C++14前，聚合类类型不能有默认的成员初始化式。C++17前，聚合不能有公共基类。
\end{tcolorbox}

\item 
帮助确定是否需要列表初始化。例如:
\begin{lstlisting}[style=styleCXX]
template<typename Coll, typename... T>
void insert(Coll& coll, T&&... val)
{
	if constexpr(!std::is_aggregate_v<typename Coll::value_type>) {
		coll.emplace_back(std::forward<T>(val)...); // invalid for aggregates
	}
	else {
		coll.emplace_back(typename Coll::value_type{std::forward<T>(val)...});
	}
}
\end{lstlisting}

\item 
要求给定的类型要么完整(见第10.3.1节)，要么是(cv限定的)void。

\item 
C++17后可用。
\end{itemize}

\textbf{std::is\_trivial<T>::value}

\begin{itemize}
\item 
若类型是一个“简单”类型，则返回true:

\begin{itemize}
\item [-]
标量类型(整型、浮点型、枚举型、指针型;参见is\_scalar())

\item [-]
简单类类型(没有虚函数、虚基类、(间接)用户定义的默认构造函数、复制/移动构造函数、复制/移动赋值操作符或析构函数、非静态数据成员没有初始化式、可变成员和非普通成员的类)

\item [-]
简单类型的数组

\item [-]
以及这些类型的cv限定类型
\end{itemize}

\item 
若is\_trivially\_copyable\_v<T>会产生true，并且存在一个简单的默认构造函数。

\item 
要求给定的类型要么完整(见第10.3.1节)，要么是(cv限定的)void。
\end{itemize}

\textbf{std::is\_trivially\_copyable<T>::value}

\begin{itemize}
\item 
若类型是“简单可复制”类型，则返回true:

\begin{itemize}
\item [-]
标量类型(整型、浮点型、枚举型、指针型;参见is\_scalar<>)

\item [-]
简单类类型(没有虚函数、虚基类、(间接)用户定义的默认构造函数、复制/移动构造函数、复制/移动赋值操作符或析构函数、非静态数据成员没有初始化式、可变成员和非普通成员的类)

\item [-]
这种类型的数组

\item [-]
以及这些类型的cv限定类型
\end{itemize}

\item 
产生与is\_trivial\_v<T>相同的结果，但可以为没有普通默认构造函数的类类型产生true。

\item 
与is\_standard \_layout<>相比，不允许可变成员，允许引用。成员可以有不同的访问权限，成员可以分布在不同的(基)类中。

\item 
要求给定的类型要么完整(见第10.3.1节)，要么是(cv限定的)void。
\end{itemize}

\textbf{std::is\_standard\_layout<T>::value}

\begin{itemize}
\item 
若该类型具有标准布局，则生成true。这样可以更容易地与其他语言交换该类型的值。

\begin{itemize}
\item [-]
标量类型(整型、浮点型、枚举型、指针型;参见is\_scalar<>)

\item [-]
标准布局类类型(没有虚函数、没有虚基类、没有非静态引用成员，所有非静态成员都在具有相同访问权限的相同(基)类中，所有成员也是标准布局类型)

\item [-]
这种类型的数组

\item [-]
以及这些类型的cv限定类型
\end{itemize}

\item 
与is\_trivial<>相比，允许可变成员，不允许引用，成员可能有相同的访问权限，成员可能不分布在不同的(基)类中。

\item 
要求给定的类型(对于数组，基本类型)要么完整(见10.3.1节)，要么是(cv限定的)void。
\end{itemize}

\textbf{std::is\_pod<T>::value}

\begin{itemize}
\item 
若T是普通的旧数据类型(POD)，则生成true。

\item 
这种类型的对象可以通过复制底层存储来复制(例如，使用memcpy())。

\item
等价于: 
\begin{lstlisting}[style=styleCXX]
is_trivial_t<T> && is_standard_layout_v<T>
\end{lstlisting}

\item
生成false的情况:

\begin{itemize}
\item [-]
没有简单的默认构造函数、复制/移动构造函数、复制/移动赋值或析构函数的类

\item [-]
具有虚成员或虚基类的类

\item [-]
具有可变成员或引用成员的类

\item [-]
不同(基)类中具有成员或具有不同访问权限的类

\item [-]
Lambda表达式的类型(称为闭包类型)

\item [-]
函数

\item [-]
void

\item [-]
由这些类型组成的类型
\end{itemize}

\item
要求给定的类型要么完整(见10.3.1节)，要么是(cv限定的)void。
\end{itemize}

\textbf{std::is\_literal\_type<T>::value}

\begin{itemize}
\item 
若给定的类型是constexpr函数的有效返回类型，则生成true(需要注意的是，该类型不包含非简单销毁的类型)。

\item 
若T是字面类型，则返回true:

\begin{itemize}
\item [-]
标量类型(整型、浮点型、枚举型、指针型;参见is\_scalar())

\item [-]
引用

\item [-]
每个(基)类中至少有一个constexpr构造函数，该构造函数不是复制/移动构造函数，在(基)类或成员中都没有用户定义或虚拟析构函数，并且对非静态数据成员的每次初始化都是常量表达式

\item [-]
这种类型的数组
\end{itemize}

\item 
要求给定的类型要么完整(见第10.3.1节)，要么是(cv限定的)void。

\item 
这个特征自C++17以来已弃用，因为“它太弱了，不能在泛型代码中有意义地使用。我们需要的是能够了解特定构造是否会产生恒定的初始化。”
\end{itemize}

\textbf{std::is\_empty<T>::value}

\begin{itemize}
\item 
若T是类类型而不是联合类型(其对象不包含数据)，则生成true。

\item 
若T定义为带有的类或结构，则生成true

\begin{itemize}
\item [-]
除了长度为0的位域外，没有非静态数据成员

\item [-]
没有虚成员函数

\item [-]
没有虚基类

\item [-]
没有非空基类
\end{itemize}

\item 
若是类/结构体，则要求给定的类型完整(见10.3.1节)(不完整的联合也可以)。
\end{itemize}

\textbf{std::is\_polymorphic<T>::value}

\begin{itemize}
\item 
若是类/结构体，则要求给定的类型完整(见10.3.1节)(不完整的联合也可以)。

\item 
要求给定的类型要么完整(见第10.3.1节)，要么既不是类也不是结构体。
\end{itemize}

\textbf{std::is\_abstract<T>::value}

\begin{itemize}
\item 
要求给定的类型要么完整(见第10.3.1节)，要么既不是类，也不是结构体。

\item 
若是类/结构体，则要求给定的类型完整(见10.3.1节)(不完整的联合也可以)。
\end{itemize}

\textbf{std::is\_final<T>::value}

\begin{itemize}
\item 
若T是final类类型(因为声明为final，该类或联合类不能作为基类)，则生成true。

\item 
对于所有非类/联合类型，如int，会返回false(这与类似于可派生不同)。

\item 
要求给定的类型T要么完整(见第10.3.1节)，要么既不是类/结构体，也不是联合。

\item 
C++14后可用。
\end{itemize}

\textbf{std::has\_virtual\_destructor<T>::value}

\begin{itemize}
\item 
若类型T具有虚析构函数，则生成true。

\item 
若是类/结构体，则要求给定的类型完整(见第10.3.1节)(不完整的联合也可以)。
\end{itemize}

\textbf{std::has\_unique\_object\_representations<T>::value}

\begin{itemize}
\item 
若任意两个T类型的对象在内存中具有相同的对象表示，则生成true。也就是，始终使用相同的字节值序列表示两个相同的值。

\item 
具有此属性的对象，可以通过哈希关联的字节序列产生可靠的哈希值(没有参与对象值的某些位可能在不同情况下不同，这没有风险)。

\item 
要求给定的类型是简单可复制的(见第D.3.1节)，并且要么是完整的(见第10.3.1节)，要么是(cv限定的)void或未知边界的数组。

\item 
C++17后可用。
\end{itemize}

\textbf{std::alignment\_of<T>::value}

\begin{itemize}
\item 
生成类型为T的对象的对齐值std::size\_t(对于数组，为元素类型;对于引用，为引用类型)。

\item 
等价于: alignof(T)

\item 
这个特性是在C++11中alignof(…)之前引入。因为特征可以作为类类型传递，对元编程很有用。

\item 
要求alignof(T)是一个有效的表达式。

\item 
使用aligned\_union<>来获得多种类型的公共对齐(参见第D.5节)。
\end{itemize}

\textbf{std::rank<T>::value}

\begin{itemize}
\item 
生成类型为T的数组的维数std::size\_t。

\item 
其他类型的结果为0。

\item 
指针没有相关的维度，数组类型中未指定的边界指定维度。(用数组类型声明的函数参数没有实际的数组类型，而且std::array也不是数组类型。参见D.2.1节。)

例如:
\begin{lstlisting}[style=styleCXX]
int a2[5][7];
rank_v<decltype(a2)>; // yields 2
rank_v<int*>; // yields 0 (no array)
extern int p1[];
rank_v<decltype(p1)>; // yields1
\end{lstlisting}
\end{itemize}

\textbf{std::extent<T>::value}

\textbf{std::extent<T, IDX>::value}

\begin{itemize}
\item 
生成类型为T的数组的第一个或第idx维的大小为std::size\_t。

\item 
若T不是数组，维度不存在，或者维度未知，则生成0。

\item 
参见第19.8.2节了解实现细节。

\begin{lstlisting}[style=styleCXX]
int a2[5][7];
extent_v<decltype(a2)>; // yields 5
extent_v<decltype(a2),0>; // yields 5
extent_v<decltype(a2),1>; // yields 7
extent_v<decltype(a2),2>; // yields 0
extent_v<int*>; // yields 0
extern int p1[];
extent_v<decltype(p1)>; // yields 0
\end{lstlisting}
\end{itemize}

\textbf{std::underlying\_type<T>::type}

\begin{itemize}
\item 
生成枚举类型T的基本类型。

\item 
要求给定的类型是完整的(见10.3.1节)枚举类型。对于所有其他类型，具有未定义行为。
\end{itemize}

\textbf{std::is\_invocable<T, Args...>::value}

\textbf{std::is\_nothrow\_invocable<T, Args...>::value}

\begin{itemize}
\item 
若T可用作Args…的可调用对象，则生成true(保证不抛出异常)。

\item 
可以使用这些特征来测试是否可以调用或std::invoke()给定Args....的可调用T(关于可调用对象和std::invoke()的详细信息请参见第11.1节。)

\item 
要求所有给定的类型都是完整的(见第10.3.1节)或(cv限定的)void或一个边界未知的数组。

\item 
例如:
\begin{lstlisting}[style=styleCXX]
struct C {
	bool operator() (int) const {
		return true;
	}
};
std::is_invocable<C>::value // false
std::is_invocable<C,int>::value // true
std::is_invocable<int*>::value // false
std::is_invocable<int(*)()>::value // true
\end{lstlisting}

\item 
C++17之后可用。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}C++17标准化过程的后期，is\_invocable改名为is\_callable。
\end{tcolorbox}
\end{itemize}

\textbf{std::is\_invocable\_r<RET\_T, T, Args...>::value}

\textbf{std::is\_nothrow\_invocable\_r<RET\_T, T, Args...>::value}

\begin{itemize}
\item 
若可以使用T作为Args…的可调用对象，结果为true(保证不抛出异常)，返回一个可转换为RET\_T类型的值。

\item 
可以使用这些特征来测试是否可以调用或std::invoke()传递给Args…的可调用T，并使用返回值RET\_T(关于可调用对象和std::invoke()的详细信息请参见第11.1节)。

\item 
要求传入的所有类型都完整(见第10.3.1节)或(cv限定的)void或未知边界的数组。

\item 
例如:
\begin{lstlisting}[style=styleCXX]
struct C {
	bool operator() (int) const {
		return true;
	}
};
std::is_invocable_r<bool,C,int>::value // true
std::is_invocable_r<int,C,long>::value // true
std::is_invocable_r<void,C,int>::value // true
std::is_invocable_r<char*,C,int>::value // false
std::is_invocable_r<long,int(*)(int)>::value // false
std::is_invocable_r<long,int(*)(int),int>::value // true
std::is_invocable_r<long,int(*)(int),double>::value // true
\end{lstlisting}

\item 
C++17后可用。
\end{itemize}

\textbf{std::invoke\_result<T, Args...>::value}

\textbf{std::result\_of<T, Args...>::value}

\begin{itemize}
\item 
生成为Args...调用可调用T的返回类型

\item 
语法略有不同:

\begin{itemize}
\item [-]
对于invoke\_result<>，必须同时传递可调用对象的类型和参数的类型作为参数。

\item [-]
对于result\_of<>，必须传递一个使用相应类型的“函数声明”。
\end{itemize}

\item 
若不能调用，则没有定义类型成员，因此使用它是一个错误(可能会在函数模板的声明中SFINAE输出使用它的函数模板;参见第8.4节)。

\item 
可以使用这些特征来获取Args...在调用或std::invoke()给定的可调用T时，获得的返回类型(关于可调用对象和std::invoke()的详细信息请参见第11.1节。)

\item 
要求所有给定的类型要么完整(见第10.3.1节)，(cv限定的)void，要么是边界未知的数组类型。

\item 
invoke\_result<>自C++17后可用，并取代了C++17弃用的result\_of<>，因为invoke\_result<>提供了一些改进，更容易的语法和接受T的抽象类型。

\item 
例如:
\begin{lstlisting}[style=styleCXX]
std::string foo(int);

using R0 = typename std::result_of<decltype(&foo)(int)>::type; // C++11
using R1 = std::result_of_t<decltype(&foo)(int)>; // C++14
using R2 = std::invoke_result_t<decltype(foo), int>; // C++17

struct ABC {
	virtual ~ABC() = 0;
	void operator() (int) const {
	}
};

using T1 = typename std::result_of<ABC(int)>::type; // ERROR: ABC is abstract
using T2 = typename std::invoke_result<ABC, int>::type; // OK since C++17
\end{lstlisting}

请参阅第11.1.3节以获得完整的示例。

\end{itemize}

\newpage

\subsubsubsection{D.3.2\hspace{0.2cm}测试具体操作}

\begin{table}[H]
	\centering
	\begin{center}
	\begin{tabular}{l|l}
		\hline
		\textbf{特性}                                                   & \textbf{作用}                                                                                                                                         \\ \hline
		is\_constructible\textless{}T,Args... \textgreater{}             & 可以用类型Args初始化类型T                                                                                                                   \\ \hline
		is\_trivially\_constructible\textless{}T,Args... \textgreater{}  & 可以用Args类型简单初始化类型T                                                                                                         \\ \hline
		is\_nothrow\_constructible\textless{}T,Args... \textgreater{}    & \begin{tabular}[c]{@{}l@{}}可以初始化类型T与类型Args，并且操作不能抛出异常\end{tabular}                                          \\ \hline
		is\_default\_constructible\textless{}T \textgreater{}            & 可以不带参数初始化T                                                                                                                      \\ \hline
		is\_trivially\_default\_constructible\textless{}T \textgreater{} & 可以不带参数地简单初始化T                                                                                                            \\ \hline
		is\_nothrow\_default\_constructible\textless{}T \textgreater{}   & \begin{tabular}[c]{@{}l@{}}可以不带参数地初始化 T，操作不能抛出异常\end{tabular}                                             \\ \hline
		is\_copy\_constructible\textless{}T \textgreater{}               & 可复制T                                                                                                                                            \\ \hline
		is\_trivially\_copy\_constructible\textless{}T \textgreater{}    & 可简单的复制T                                                                                                                                  \\ \hline
		is\_nothrow\_copy\_constructible\textless{}T \textgreater{}      & 是否可以复制一个T，而操作不能抛出异常                                                                                                             \\ \hline
		is\_move\_constructible\textless{}T \textgreater{}               & 可以移动T                                                                                                                                            \\ \hline
		is\_trivially\_move\_constructible\textless{}T \textgreater{}    & 可以简单移动T                                                                                                                              \\ \hline
		is\_nothrow\_move\_constructible\textless{}T \textgreater{}      & 可以移动一个T，而那个操作不能抛出异常                                                                                                             \\ \hline
		is\_assignable\textless{}T,T2 \textgreater{}                     & 将T2赋值给T                                                                                                                            \\ \hline
		is\_trivially\_assignable\textless{}T,T2 \textgreater{}          & 简单地将T2赋值给T                                                                                                                  \\ \hline
		is\_nothrow\_assignable\textless{}T,T2 \textgreater{}            & \begin{tabular}[c]{@{}l@{}}可以将类型T2赋值给类型T，而该操作不抛出异常\end{tabular}                                                   \\ \hline
		is\_copy\_assignable\textless{}T \textgreater{}                  & 复制赋值给T                                                                                                                                     \\ \hline
		is\_trivially\_copy\_assignable\textless{}T \textgreater{}       & 简单的复制赋值给T                                                                                                                            \\ \hline
		is\_nothrow\_copy\_assignable\textless{}T \textgreater{}         & 复制赋值操作不能抛出异常                                                                                                      \\ \hline
		is\_move\_assignable\textless{}T \textgreater{}                  & 移动赋值给T                                                                                                                                     \\ \hline
		is\_trivially\_move\_assignable\textless{}T \textgreater{}       & 简单的移动赋值给T                                                                                                                           \\ \hline
		is\_nothrow\_move\_assignable\textless{}T \textgreater{}         & 移动赋值T，不能抛出异常                                                                                                     \\ \hline 
		is\_destructible\textless{}T \textgreater{}                      & 可销毁T                                                                                                                                         \\ \hline
		is\_trivially\_destructible\textless{}T \textgreater{}           & 可简单的销毁T                                                                                                                               \\ \hline
		is\_nothrow\_destructible\textless{}T \textgreater{}             & 销毁T，而不抛出异常                                                                                               \\ \hline
		is\_swappable\textless{}T \textgreater{}                         & 可以对该类型使用swap()(C++ 17)                                                                                                             \\ \hline
		is\_nothrow\_swappable\textless{}T \textgreater{}                & \begin{tabular}[c]{@{}l@{}}可以对该类型调用swap()，而不能抛出异常(C++17)\end{tabular}                                    \\ \hline
		is\_swappable\_with\textless{}T,T2 \textgreater{}                & \begin{tabular}[c]{@{}l@{}}可以为这两种类型调用swap()与特定的\\ 值类别(C++17)\end{tabular}                                \\ \hline
		is\_nothrow\_swappable\_with\textless{}T,T2 \textgreater{}       & \begin{tabular}[c]{@{}l@{}}可以为这两种类型调用swap()与特定的值\\类别，该操作不能抛出异常(C++17)\end{tabular} \\ \hline
	\end{tabular}
	\end{center}
\end{table}

\begin{center}
表D.4. 检查特定操作的特征
\end{center}

表D.4列出了允许检查某些特定操作的类型特征。is\_trivially\_…，检查对象、成员或基类调用的所有(子)操作是否简单(既不是用户定义的，也不是虚的)。is\_nothrow\_…，检查调用的操作是否保证不抛出异常，所有is\_…\_constructible检查对应的is\_…\_destructible。例如:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{utils/isconstructible.cpp}
\begin{lstlisting}[style=styleCXX]
#include <iostream>

class C {
	public:
	C() { // default constructor has no noexcept
	}
	virtual ~C() = default; // makes C nontrivial
};

int main()
{
	using namespace std;
	cout << is_default_constructible_v<C> << ’\n’; // true
	cout << is_trivially_default_constructible_v<C> << ’\n’; // false
	cout << is_nothrow_default_constructible_v<C> << ’\n’; // false
	cout << is_copy_constructible_v<C> << ’\n’; // true
	cout << is_trivially_copy_constructible_v<C> << ’\n’; // true
	cout << is_nothrow_copy_constructible_v<C> << ’\n’; // true
	cout << is_destructible_v<C> << ’\n’; // true
	cout << is_trivially_destructible_v<C> << ’\n’; // false
	cout << is_nothrow_destructible_v<C> << ’\n’; // true
}
\end{lstlisting}

因虚构造函数的定义，所有操作都不再简单。因为我们定义了一个默认构造函数，没有使用noexcept，所以可能会抛出异常。默认情况下，所有其他操作都不会抛出异常。

\textbf{std::is\_constructible<T, Args...>::value}

\textbf{std::is\_trivially\_constructible<T, Args...>::value}

\textbf{std::is\_nothrow\_constructible<T, Args...>::value}

\begin{itemize}
\item 
若T类型的对象可以用Args给出的参数类型初始化，则生成true。(不使用非简单操作或保证不抛出异常)。也就是说，以下代码有效:

\begin{lstlisting}[style=styleCXX]
T t(std::declval<Args>()...);
\end{lstlisting}

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}关于std::declval的效果，请参见第11.2.3节
\end{tcolorbox}

\item
true意味着对象可以销毁(is\_destructible\_v<T>, is\_trivially\_destructible\_v<T>， 或is\_nothrow\_destructible\_v<T>的结果为true)。

\item
要求所有给定的类型要么完整(见第10.3.1节)，要么是(cv限定的)void，要么是边界未知的数组。

\item
例如:
\begin{lstlisting}[style=styleCXX]
is_constructible_v<int> // true
is_constructible_v<int,int> // true
is_constructible_v<long,int> // true
is_constructible_v<int,void*> // false
is_constructible_v<void*,int> // false
is_constructible_v<char const*,std::string> // false
is_constructible_v<std::string,char const*> // true
is_constructible_v<std::string,char const*,int,int> // true
\end{lstlisting}

\item
注意，is\_convertible对源类型和目标类型有不同的顺序。
\end{itemize}

\textbf{std::is\_default\_constructible<T>::value}

\textbf{std::is\_trivially\_default\_constructible<T>::value}

\textbf{std::is\_nothrow\_default\_constructible<T>::value}

\begin{itemize}
\item 
若T类型的对象可以初始化，而不需要初始化参数(不需要使用非简单操作或保证不抛出异常)，则生成true。

\item 
与is\_constructible\_v<T>, is\_trivially\_constructible\_v<T>或 is\_nothrow\_constructible\_v<T>相同。

\item 
true意味着可以销毁对象 (例如，is\_destructible\_v<T>, is\_trivially\_destructible\_v<T>或 is\_nothrow\_destructible\_v<T>生成true)。

\item 
要求给定的类型要么完整(见第10.3.1节)，(cv限定的)void，要么是一个边界未知的数组。
\end{itemize}

\textbf{std::is\_copy\_constructible<T>::value}

\textbf{std::is\_trivially\_copy\_constructible<T>::value}

\textbf{std::is\_nothrow\_copy\_constructible<T>::value}

\begin{itemize}
\item 
若T类型的对象可以通过复制T类型的另一个值来创建(无需使用非简单操作或保证不抛出异常)，则生成true。

\item 
若T不是可引用类型，则生成false(可以是(cv限定的)void，也可以是const、volatile、\&和/或\&\&限定的函数类型)。

\item 
假设T是一个可引用的类型，就像is\_constructible<T, T const\&>::value, is\_trivially\_constructible<T, T const\&>::value或is\_nothrow\_constructible<T, T const\&>::value一样。

\item 
要找出T类型的对象是否可以从类型T的右值复制构造，可使用is\_constructible<T, T\&\&>。

\item 
true意味着可以销毁对象(例如，is\_destructible\_v<T>，is\_trivially\_destructible\_v<T>或is\_nothrow\_destructible\_v<T>生成true)。

\item 
要求给定的类型要么完整(见第10.3.1节)，(cv限定的)void，要么是一个边界未知的数组。

\item 
例如:
\begin{lstlisting}[style=styleCXX]
is_copy_constructible_v<int> // yields true
is_copy_constructible_v<void> // yields false
is_copy_constructible_v<std::unique_ptr<int>> // yields false
is_copy_constructible_v<std::string> // yields true
is_copy_constructible_v<std::string&> // yields true
is_copy_constructible_v<std::string&&> // yields false
// in contrast to:
is_constructible_v<std::string,std::string> // yields true
is_constructible_v<std::string&,std::string&> // yields true
is_constructible_v<std::string&&,std::string&&> // yields true
\end{lstlisting}

\end{itemize}

\textbf{std::is\_move\_constructible<T>::value}

\textbf{std::is\_trivially\_move\_constructible<T>::value}

\textbf{std::is\_nothrow\_move\_constructible<T>::value}

\begin{itemize}
\item 
若T类型的对象可以由T类型的右值创建(无需使用非简单操作或保证不抛出异常)，则生成true。

\item 
若T不是可引用类型((cv限定的)void或使用const、volatile、\&和/或\&\&限定的函数类型)，则生成false。

\item 
假设T是一个可引用的类型，就像is\_constructible<T,T\&\&>::value，is\_trivially\_constructible<T,T\&\&>::value，或者is\_nothrow\_constructible<T,T\&\&>::value一样。

\item 
true意味着对象可以销毁(即，is\_destructible\_v<T>，is\_trivially\_destructible\_v<T>，或者is\_nothrow\_destructible\_v<T>生成true)。

\item 
若不能为T类型的对象直接调用移动构造函数，就没有办法检查它是否可以抛出异常。构造函数为public，而不删除不行;还要求对应的类型不是抽象类(对抽象类的引用或指针可以正常工作)。

\item 
参见19.7.2节了解实现细节。

\item 
例如:
\begin{lstlisting}[style=styleCXX]
is_move_constructible_v<int> // yields true
is_move_constructible_v<void> // yields false
is_move_constructible_v<std::unique_ptr<int>> // yields true
is_move_constructible_v<std::string> // yields true
is_move_constructible_v<std::string&> // yields true
is_move_constructible_v<std::string&&> // yields true
// in contrast to:
is_constructible_v<std::string,std::string> // yields true
is_constructible_v<std::string&,std::string&> // yields true
is_constructible_v<std::string&&,std::string&&> // yields true
\end{lstlisting}
\end{itemize}

\textbf{std::is\_assignable<TO, FROM>::value}

\textbf{std::is\_trivially\_assignable<TO, FROM>::value}

\textbf{std::is\_nothrow\_assignable<TO, FROM>::value}

\begin{itemize}
\item
若可以将FROM类型的对象赋值给TO类型的对象(无需使用重要操作或保证不抛出异常)，则生成true。

\item
要求给定的类型要么完整(见第10.3.1节)，(cv限定的)void，要么是边界未知的数组。

\item
作为第一类型的非引用非类类型is\_assignable\_v<>总是会产生false，因为这样的类型会产生prvalues。也就是说，42 = 77;不是有效的。对于类类型，只要给定适当的赋值操作符，就可以对右值赋值(这是由于旧的规则，可以对类类型的右值调用非const成员函数)。

\item
is\_convertible对源类型和目标类型有不同的顺序。

\item
例如:
\begin{lstlisting}[style=styleCXX]
is_assignable_v<int,int> // yields false
is_assignable_v<int&,int> // yields true
is_assignable_v<int&&,int> // yields false
is_assignable_v<int&,int&> // yields true
is_assignable_v<int&&,int&&> // yields false
is_assignable_v<int&,long&> // yields true
is_assignable_v<int&,void*> // yields false
is_assignable_v<void*,int> // yields false
is_assignable_v<void*,int&> // yields false
is_assignable_v<std::string,std::string> // yields true
is_assignable_v<std::string&,std::string&> // yields true
is_assignable_v<std::string&&,std::string&&> // yields true
\end{lstlisting}
\end{itemize}

\textbf{std::is\_copy\_assignable<T>::value}

\textbf{std::is\_trivially\_copy\_assignable<T>::value}

\textbf{std::is\_nothrow\_copy\_assignable<T>::value}

\begin{itemize}
\item
若T类型的值可以(复制-)赋值给T类型的对象(无需使用非简单操作或保证不抛出异常)，则生成true。

\item
若T不是可引用类型((cv限定的)void或使用const、volatile、\&和/或\&\&限定的函数类型)，则生成false。
 
\item
假设T是一个可引用的类型，就和is\_assignable<T\&, T const\&>::value, is\_trivially\_assignable<T\&, T const\&>::value或is\_nothrow\_assignable<T\&, T const\&>::value一样。

\item
要找出是否可以将类型T的右值复制赋值给另一个类型T的右值，要使用is\_assignable<T\&\&, T\&\&>。

\item
void、内置数组类型和带有删除复制赋值操作符的类，不能复制赋值。

\item
要求给定的类型要么完整(见第10.3.1节)，(cv限定的)void，要么是一个边界未知的数组。

\item
例如:
\begin{lstlisting}[style=styleCXX]
is_copy_assignable_v<int> // yields true
is_copy_assignable_v<int&> // yields true
is_copy_assignable_v<int&&> // yields true
is_copy_assignable_v<void> // yields false
is_copy_assignable_v<void*> // yields true
is_copy_assignable_v<char[]> // yields false
is_copy_assignable_v<std::string> // yields true
is_copy_assignable_v<std::unique_ptr<int>> // yields false
\end{lstlisting}
\end{itemize}

\textbf{std::is\_move\_assignable<T>::value}

\textbf{std::is\_trivially\_move\_assignable<T>::value}

\textbf{std::is\_nothrow\_move\_assignable<T>::value}

\begin{itemize}
\item
若T类型的右值可以移动赋值给T类型的对象(无需使用非简单操作或保证不抛出异常)，则生成true。

\item
若T不是可引用类型((cv限定的)void或使用const、volatile、\&和/或\&\&限定的函数类型)，则生成false。

\item
假设T是一个可引用的类型，就像is\_assignable<T\&, T\&\&>::value, is\_trivially\_assignable<T\&, T\&\&>::value或is\_nothrow\_assignable<T\&, T\&\&>::value一样。

\item
void、内置数组类型和带有删除移动赋值操作符的类不能进行移动赋值。

\item
要求给定的类型要么完整(见第10.3.1节)，要么是(cv限定的)void或一个边界未知的数组。

\item
例如:
\begin{lstlisting}[style=styleCXX]
is_move_assignable_v<int> // yields true
is_move_assignable_v<int&> // yields true
is_move_assignable_v<int&&> // yields true
is_move_assignable_v<void> // yields false
is_move_assignable_v<void*> // yields true
is_move_assignable_v<char[]> // yields false
is_move_assignable_v<std::string> // yields true
is_move_assignable_v<std::unique_ptr<int>> // yields true
\end{lstlisting}
\end{itemize}

\textbf{std::is\_swappable\_with<T1, T2>::value}

\textbf{std::is\_nothrow\_swappable\_with<T1, T2>::value}

\begin{itemize}
\item
若类型T1的表达式可以与类型T2的表达式交换，则生成true，除非引用类型只确定表达式的值类别(保证不抛出异常)。

\item
要求给定的类型要么完整(见第10.3.1节)，要么是(cv限定的)void或边界未知的数组。

\item
作为第一或第二种类型的非引用、非类类型is\_swappable\_with\_v<>总是会产生false，因为这样的类型会产生prvalue。也就是说，swap(42,77)无效。

\item
例如:
\begin{lstlisting}[style=styleCXX]
is_swappable_with_v<int,int> // yields false
is_swappable_with_v<int&,int> // yields false
is_swappable_with_v<int&&,int> // yields false
is_swappable_with_v<int&,int&> // yields true
is_swappable_with_v<int&&,int&&> // yields false
is_swappable_with_v<int&,long&> // yields false
is_swappable_with_v<int&,void*> // yields false
is_swappable_with_v<void*,int> // yields false
is_swappable_with_v<void*,int&> // yields false
is_swappable_with_v<std::string,std::string> // yields false
is_swappable_with_v<std::string&,std::string&> // yields true
is_swappable_with_v<std::string&&,std::string&&> // yields false
\end{lstlisting}

\item
C++17后可用。
\end{itemize}

\textbf{std::is\_swappable<T>::value}

\textbf{std::is\_nothrow\_swappable<T>::value}

\begin{itemize}
\item
若T类型的左值可以交换(保证不抛出异常)，则生成true。

\item
假设T是一个可引用的类型。就像 is\_swappable\_with<T\&, T\&>::value或is\_nothrow\_swappable\_with<T\&, T\&>::value一样。

\item
若T不是可引用类型((cv限定的)void或使用const、volatile、\&和/或\&\&限定的函数类型)，则生成false。

\item
要找出一个T的右值是否可以与另一个T的右值交换，请使用is\_swappable\_with<T\&\&, T\&\&>。

\item
要求给定的类型是一个完整的类型(第10.3.1节)、(cv限定的)void或一个边界未知的数组。

\item
例如:
\begin{lstlisting}[style=styleCXX]
is_swappable_v<int> // yields true
is_swappable_v<int&> // yields true
is_swappable_v<int&&> // yields true
is_swappable_v<std::string&&> // yields true
is_swappable_v<void> // yields false
is_swappable_v<void*> // yields true
is_swappable_v<char[]> // yields false
is_swappable_v<std::unique_ptr<int>> // yields true
\end{lstlisting}

\item
C++17后可用。
\end{itemize}

\subsubsubsection{D.3.3\hspace{0.2cm}类型之间的关系}

表D.5列出了允许测试类型之间特定关系的类型特征，包括检查为类类型提供了哪些构造函数和赋值操作符。

\begin{table}[H]
	\begin{center}
	\begin{tabular}{l|l}
		\hline
		\textbf{特性}                                & \textbf{作用}                                                    \\ \hline
		is\_same\textless{}T1,T2 \textgreater{}       & T1和T2是相同的类型(包括const/volatile限定符)\\ \hline
		is\_base\_of\textless{}T,D \textgreater{}     & 类型T是类型D的基类                                     \\ \hline
		is\_convertible\textless{}T,T2 \textgreater{} & 类型T可以转换为类型T2                                 \\ \hline
	\end{tabular}
	\end{center}
\end{table}

\begin{center}
表D.5. 测试类型关系的特征
\end{center}

\textbf{std::is\_same<T1, T2>::value}

\begin{itemize}
\item
若T1和T2命名相同的类型，包括cv限定符(const和volatile)，则生成true。

\item
若一个类型是另一个类型的别名，则生成true。

\item
若两个对象由相同类型的对象初始化，则生成true。

\item
对于与两个不同的Lambda表达式关联的(闭包)类型，即使定义了相同的行为，也会产生false。

\item
例如:
\begin{lstlisting}[style=styleCXX]
auto a = nullptr;
auto b = nullptr;
is_same_v<decltype(a),decltype(b)> // yields true

using A = int;
is_same_v<A,int> // yields true

auto x = [] (int) {};
auto y = x;
auto z = [] (int) {};
is_same_v<decltype(x),decltype(y)> // yields true
is_same_v<decltype(x),decltype(z)> // yields false
\end{lstlisting}

\item
参见第19.3.3节了解实现细节。
\end{itemize}

\textbf{std::is\_base\_of<B, D>::value}

\begin{itemize}
\item
若B是D的基类或B与D是同一个类，则为true。

\item
类型是cv限定的、私有的还是受保护的继承并不重要，D有多个B的基类，或者D通过多个继承(通过虚拟继承)将B作为基类。

\item
若至少有一个类型是联合类型，则生成false。

\item
要求类型D要么是完整的(见第10.3.1节)，与B具有相同的类型(忽略任何const/volatile限定)，要么既不是结构体也不是类。

\item
例如:
\begin{lstlisting}[style=styleCXX]
class B {
};
class D1 : B {
};
class D2 : B {
};
class DD : private D1, private D2 {
};
is_base_of_v<B, D1> // yields true
is_base_of_v<B, DD> // yields true
is_base_of_v<B const, DD> // yields true
is_base_of_v<B, DD const> // yields true
is_base_of_v<B, B const> // yields true
is_base_of_v<B&, DD&> // yields false (no class type)
is_base_of_v<B[3], DD[3]> // yields false (no class type)
is_base_of_v<int, int> // yields false (no class type)
\end{lstlisting}
\end{itemize}

\textbf{std::is\_convertible<FROM, TO>::value}

\begin{itemize}
\item
若FROM类型的表达式可转换为TO类型，则生成true。因此，以下代码有效:

\begin{lstlisting}[style=styleCXX]
TO test() {
	return std::declval<FROM>();
}
\end{lstlisting}

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}关于std::declval的效果，请参见第11.2.3节。
\end{tcolorbox}

\item
类型FROM之上的引用仅用于确定转换表达式的值类别;基础类型就是源表达式的类型。

\item
is\_constructible并不总等价于is\_convertible。例如:

\begin{lstlisting}[style=styleCXX]
class C {
	public:
	explicit C(C const&); // no implicit copy constructor
	...
};
is_constructible_v<C,C> // yields true
is_convertible_v<C,C> // yields false
\end{lstlisting}

\item
要求给定的类型要么完整(见第10.3.1节)，要么是(cv限定的)void或是边界未知的数组。

\item
s\_constructible(参见D.3.2节)和is\_assignable(参见D.3.2节)对于源和目标类型有不同的顺序。

\item
参见第19.5节了解实现细节。
\end{itemize}